home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 8614 / 8614.xpi / modules / utils / Utils.jsm < prev    next >
Text File  |  2010-02-10  |  24KB  |  801 lines

  1. // DO NOT import this into the global namespace, but instead
  2. // import it into your own namespace wrapper
  3.  
  4. var EXPORTED_SYMBOLS = ["Utils"];
  5.  
  6. Components.utils.import("resource://glydo/utils/prototype_xul_1_6_0_3_modified.jsm");
  7.  
  8. var window = Components.classes["@mozilla.org/appshell/appShellService;1"]
  9.                                 .getService(Components.interfaces.nsIAppShellService)
  10.                                 .hiddenDOMWindow;
  11.  
  12. var document = window.document;
  13.  
  14. var Utils = {
  15.  
  16.     unescapeHTMLIfNecessary: function(s) {
  17.         try {
  18.             return Prototype.S.unescapeHTML(s);
  19.         } catch (ignore) {
  20.             return s;
  21.         }
  22.     },
  23.  
  24.     setTimeout: function(func, delay)    {
  25.         var timer = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer);
  26.         var callback = { that: this, notify: function () { func.call(this.that); } };
  27.         timer.initWithCallback(callback, delay, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
  28.         return timer;
  29.     },
  30.  
  31.     clearTimeout: function(timer) {
  32.         timer.cancel();
  33.     },
  34.  
  35.     setInterval: function(func, delay) {
  36.         var timer = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer);
  37.         var callback = { that: this, notify: function () { func.call(this.that); } };
  38.         timer.initWithCallback(callback, delay, Components.interfaces.nsITimer.TYPE_REPEATING_PRECISE);
  39.         return timer;
  40.     },
  41.  
  42.     clearInterval: function(timer) {
  43.         timer.cancel();
  44.     },
  45.  
  46.     arrayify: function(obj) {
  47.         if (obj === null || obj === undefined) {
  48.             return [];
  49.         }
  50.         if (Prototype.O.isArray(obj)) {
  51.             return obj;
  52.         }
  53.         return [obj];
  54.     },
  55.     
  56.     compareVersionNumbers: function(v1,v2) {
  57.         var v1parts = v1.split(".");
  58.         var v2parts = v2.split(".");
  59.         var l = Math.max(v1parts.length,v2parts.length);
  60.         for (var i = 0; i < l; ++i) {
  61.             var p1 = v1parts[i] || '0';
  62.             var p2 = v2parts[i] || '0';
  63.             if (p1 !== p2) {
  64.                 var parts1 = /^([0-9]*)([^0-9]*)([0-9]*)([^0-9]*)$/.exec(p1);
  65.                 if (parts1 === null) {
  66.                     throw "Not a valid version number: " + v1;
  67.                 }
  68.                 var parts2 = /^([0-9]*)([^0-9]*)([0-9]*)([^0-9]*)$/.exec(p2);
  69.                 if (parts2 === null) {
  70.                     throw "Not a valid version number: " + v2;
  71.                 }
  72.                 for (var j = 1; j <= 4; ++j) {
  73.                     var c = this.compareVersionNumberPartPart(parts1[j],parts2[j],!(j & 1));
  74.                     if (c > 0) {
  75.                         return 1;
  76.                     } 
  77.                     if (c < 0) {
  78.                         return -1;
  79.                     }
  80.                 }
  81.             }
  82.         }
  83.         return 0;
  84.     },
  85.     
  86.     compareVersionNumberPartPart: function(p1,p2,stringcomp) {
  87.         if (!stringcomp) {
  88.             // Numeric part comparison
  89.             p1 = parseInt(p1);
  90.             p2 = parseInt(p2);
  91.         } else {
  92.             // String part comparison
  93.             if (p1 === p2) {
  94.                 return 0;
  95.             }
  96.             // If a string part is missing, it is greater than the other
  97.             if (!p1 && p2) {
  98.                 return -1;
  99.             }
  100.             if (!p2 && p1) {
  101.                 return 1;
  102.             }
  103.         }
  104.         // Compare the two parts (as numbers of bytes, depending on
  105.         // the previous manipulations
  106.         if (p1 < p2) {
  107.             return 1;
  108.         }
  109.         if (p1 > p2) {
  110.             return -1;
  111.         }
  112.         return 0
  113.     },
  114.     
  115.     uuid1: function() {
  116.         var uuidGenerator = 
  117.           Components.classes["@mozilla.org/uuid-generator;1"]
  118.                     .getService(Components.interfaces.nsIUUIDGenerator);
  119.         var uuid = uuidGenerator.generateUUID();
  120.         return uuid.toString().slice(1,-1);
  121.     },
  122.  
  123.     shuffle: function(values) {
  124.         var l = values.length;
  125.         for (var i = 0; i < l-1; ++i) {
  126.             var j = Math.floor(Math.random()*(l-i))+i;
  127.             var t = values[i];
  128.             values[i] = values[j];
  129.             values[j] = t;
  130.         }
  131.     },
  132.     
  133.     promptService: function() {
  134.         var service = Components.classes["@mozilla.org/embedcomp/prompt-service;1"];
  135.         return service.getService(Components.interfaces.nsIPromptService);
  136.     },
  137.     
  138.     platformLong: function() {
  139.         return window.navigator.platform.toLowerCase();
  140.     },
  141.     
  142.     platformShort: function() {
  143.         var platform = this.platformLong();
  144.     
  145.         if (platform.indexOf('linux') != -1) {
  146.             return 'linux';
  147.         }
  148.     
  149.         if (platform.indexOf('mac') != -1) {
  150.             return 'mac';
  151.         }
  152.     
  153.         if (platform.indexOf('win') != -1) {
  154.             return 'windows';
  155.         }
  156.         
  157.         return 'unknown';
  158.     },
  159.     
  160.     containerAppVersion: function() {
  161.         var ver = null;
  162.         try {
  163.             ver = APP_INFO.version;
  164.         } catch (e) {
  165.         }
  166.         return ver;
  167.     },
  168.     
  169.     appName: function() {
  170.         var name = "unknown";
  171.         try {
  172.             var id = APP_INFO.ID;
  173.             switch (id) {
  174.                 case "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}":
  175.                     name = "firefox";
  176.                     break;
  177.                 case "{a463f10c-3994-11da-9945-000d60ca027b}":
  178.                     name = "flock";
  179.                     break;
  180.             }
  181.         } catch (e) {
  182.         }
  183.     
  184.         return name;
  185.     },
  186.     
  187.     getMostRecentWindow: function(type) {
  188.         var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
  189.             .getService(Components.interfaces.nsIWindowMediator);
  190.         return wm.getMostRecentWindow(type);
  191.     },
  192.     
  193.     extensionPath: function(appid) {
  194.         return Components.classes["@mozilla.org/extensions/manager;1"].getService(
  195.             Components.interfaces.nsIExtensionManager).getInstallLocation(appid)
  196.             .getItemLocation(appid);
  197.     },
  198.     
  199.     extensionVersion: function(id) {
  200.         var ver = null;
  201.         try {
  202.             var rdf = Components.classes["@mozilla.org/rdf/rdf-service;1"]
  203.                 .getService(Components.interfaces.nsIRDFService);
  204.             var managerSource = Components.classes["@mozilla.org/extensions/manager;1"]
  205.                 .getService(Components.interfaces.nsIExtensionManager).datasource;
  206.             var extension = rdf.GetResource("urn:mozilla:item:" + id);
  207.             var versionResource = rdf
  208.                 .GetResource("http://www.mozilla.org/2004/em-rdf#version");
  209.             var versionItem = managerSource.GetTarget(extension, versionResource, true);
  210.             ver = versionItem.QueryInterface(Components.interfaces.nsIRDFLiteral).Value;
  211.         } catch (e) {
  212.         }
  213.         return ver;
  214.     },
  215.     
  216.     addListener: function(element, event, f, context, capture) {
  217.         element.addEventListener(event, Prototype.F.bindAsEventListener(f,context), capture);
  218.     },
  219.     
  220.     newURI: function(spec,base) {
  221.         var service = Components.classes["@mozilla.org/network/io-service;1"].
  222.             getService(Components.interfaces.nsIIOService);
  223.         if (base != null) {
  224.             if (Prototype.O.isString(base)) {
  225.                 base = service.newURI(base,null,null);
  226.             }
  227.         }
  228.         return service.newURI(spec,null,base);        
  229.     },
  230.     
  231.     getURLHost: function(url) {
  232.         var re = new RegExp(
  233.             "^(?:[a-zA-Z][a-zA-Z0-9+-.]*://)?(?:[^@/]*@)?([^:@/]*)(?::[0-9]*)?(?:/.*)?$");
  234.         var match = re.exec(url);
  235.         if (match == null) {
  236.             return null;
  237.         }
  238.         return match[1];
  239.     },
  240.     
  241.     getSiteName: function(url) {
  242.         var host = this.getURLHost(url);
  243.         if (host == undefined) {
  244.             return undefined;
  245.         }
  246.         if (host == null) {
  247.             return null;
  248.         }
  249.         return host.replace(new RegExp("^www\\."), "");
  250.     },
  251.     
  252.     getHighLevelDomainName: function(url) {
  253.         var site = this.getSiteName(url);
  254.         if (site === null) {
  255.             return null;
  256.         }
  257.         var lastDotIndex = site.lastIndexOf('.');
  258.         if (lastDotIndex == -1) {
  259.             return null;
  260.         }
  261.         var tld = site.substring(lastDotIndex + 1).toLowerCase();
  262.         if (tld.search(/^\d+$/) != -1) {
  263.             return null;
  264.         }
  265.         var nParts = Utils.HIGH_LEVEL_DOMAIN_PARAMS.domainPartsPerTLD[tld];
  266.         if (nParts === undefined) {
  267.             nParts = Utils.HIGH_LEVEL_DOMAIN_PARAMS.nDomainPartsUnrecognizedTLD;
  268.         }
  269.         for ( var nPartsFound = 1; nPartsFound < nParts; ++nPartsFound) {
  270.             lastDotIndex = site.lastIndexOf(".", lastDotIndex - 1);
  271.             if (lastDotIndex == -1) {
  272.                 break;
  273.             }
  274.         }
  275.         return site.substring(lastDotIndex + 1);
  276.     },
  277.  
  278.     getFaviconURLs: function(url,website) {
  279.         var w = website || Utils.getHighLevelDomainName(url);
  280.         var index = w.indexOf('/');
  281.         if (index != -1) {
  282.             w = w.substr(0,index);
  283.         }
  284.         var res = [
  285.                         "http://www." + w + "/favicon.ico",
  286.                         "http://" + w + "/favicon.ico"];
  287.         // Patch for denver post
  288.         if (w == "denverpost.com") {
  289.             res.push("http://extras.mnginteractive.com/live/media/favIcon/dpo/favicon.ico");
  290.         }
  291.         if (!website) {
  292.             var h = Utils.getURLHost(url);
  293.             if ((h != w) && (h != "www." + w)) {
  294.                 res.push("http://" + h + "/favicon.ico");
  295.             }
  296.         }
  297.         return res;
  298.     },
  299.     
  300.     scaleClipAndCenterImageInBox: function(targetWidth,targetHeight,imgNode,image) {
  301.         if (imgNode.src != image.src) {
  302.             imgNode.src = image.src;
  303.         }
  304.         var xFactor = targetWidth / image.width;
  305.         var yFactor = targetHeight / image.height;
  306.         var factor = Math.max(Math.min(xFactor,yFactor,Utils.IMAGE_SCALING_CONSTS.MAX_THUMBNAIL_SCALING_FACTOR),Utils.IMAGE_SCALING_CONSTS.MIN_THUMBNAIL_SCALING_FACTOR);
  307.         var width = Math.round(image.width * factor);
  308.         var height = Math.round(image.height * factor);
  309.         var t = (targetHeight - height)/2;
  310.         var l = (targetWidth - width)/2;
  311.         Prototype.E.setStyle(imgNode,{
  312.             width: width + "px",
  313.             height: height + "px",
  314.             position: "absolute",
  315.             top: t + "px",
  316.             left: l + "px"});
  317.         imgNode.width = width;
  318.         imgNode.height = height;
  319.     },
  320.  
  321.     toISO8601DateString: function(date) {
  322.         var du = date.getTime();
  323.         du += date.getTimezoneOffset()*60000;
  324.         var ms = date.getUTCMilliseconds();
  325.         return (new Date(du)).toLocaleFormat(Utils.ISO8601_DATE_FORMAT) + "." + ms + "Z";
  326.     },
  327.     
  328.     parseISO8601Date: function(dateString) {
  329.         var match = Utils.ISO8601_DATE_PATTERN.exec(dateString);
  330.         if (match === null) {
  331.             return null;
  332.         }
  333.         var year = parseInt(match[1], 10);
  334.         var month = parseInt(match[2], 10) - 1;
  335.         var day = parseInt(match[3], 10);
  336.         var hour = parseInt(match[4], 10);
  337.         var minute = parseInt(match[5], 10);
  338.         var second = parseInt(match[6], 10);
  339.         var milli = 0;
  340.         if (match[7]) {
  341.             milli = parseInt(match[7], 10);
  342.         }
  343.         var tzSign = "+";
  344.         var tzHour = 0;
  345.         var tzMinute = 0;
  346.         if (match[8] != "Z") {
  347.             tzSign = match[9];
  348.             tzHour = parseInt(match[10], 10);
  349.             tzMinute = parseInt(match[11], 10);
  350.         }
  351.         var d = Date.UTC(year, month, day, hour, minute, second, milli);
  352.         var offset = (tzHour * 3600 + tzMinute * 60) * 1000;
  353.         if (tzSign == "-") {
  354.             offset = -offset;
  355.         }
  356.         d -= offset;
  357.         return new Date(d);
  358.     },
  359.     
  360.     dateDistanceInWords: function(from_time, to_time, localizedStrings) {
  361.         var from_s = from_time.getTime() / 1000.0;
  362.         var to_s = to_time.getTime() / 1000.0;
  363.         var distance_in_minutes = Math.round(Math.abs(to_s - from_s) / 60);
  364.         var distance_in_seconds = Math.round(Math.abs(to_s - from_s));
  365.     
  366.         if (distance_in_minutes <= 1) {
  367.             return localizedStrings.get("general.dates.intervals.less_than_one_minute");
  368.         } else if (distance_in_minutes <= 59) {
  369.             return localizedStrings.get("general.dates.intervals.minutes",
  370.                 [ distance_in_minutes ]);
  371.         } else if (distance_in_minutes <= 1439) {
  372.             return localizedStrings.get("general.dates.intervals.hours", [ Math
  373.                 .round(distance_in_minutes / 60.0) ]);
  374.         } else if (distance_in_minutes <= 43199) {
  375.             return localizedStrings.get("general.dates.intervals.days", [ Math
  376.                 .round(distance_in_minutes / 1440.0) ]);
  377.         } else if (distance_in_minutes <= 525599) {
  378.             return localizedStrings.get("general.dates.intervals.months", [ Math
  379.                 .round(distance_in_minutes / 43200.0) ]);
  380.         } else {
  381.             return localizedStrings.get("general.dates.intervals.years", [ Math
  382.                 .round(distance_in_minutes / 525600.0) ]);
  383.         }
  384.     },
  385.     
  386.     dateInWords: function(from_time, to_time,
  387.         localizedStrings) {
  388.         var from_s = from_time.getTime() / 1000.0;
  389.         var to_s = to_time.getTime() / 1000.0;
  390.         var distance_in_minutes = Math.round(Math.abs(to_s - from_s) / 60);
  391.         var distance_in_seconds = Math.round(Math.abs(to_s - from_s));
  392.     
  393.         if (distance_in_minutes < 1) {
  394.             return localizedStrings.get("general.dates.intervals.less_than_one_minute_ago");
  395.         } else if (distance_in_minutes < 60) {
  396.             return localizedStrings.get("general.dates.ago", [ localizedStrings.get(
  397.                 "general.dates.intervals.minutes", [ distance_in_minutes ]) ]);
  398.         } else if (distance_in_minutes < 90) {
  399.             return localizedStrings.get("general.dates.ago", [ localizedStrings.get(
  400.                 "general.dates.intervals.hour", [ 1 ]) ]);
  401.         } else if (distance_in_minutes < 1440 / 2) {
  402.             return localizedStrings.get("general.dates.ago", [ localizedStrings.get(
  403.                 "general.dates.intervals.hours", [ Math
  404.                     .round(distance_in_minutes / 60.0) ]) ]);
  405.         } else {
  406.             // Go by day in week
  407.             var from_day = Math.floor(from_s / (24 * 60 * 60));
  408.             var to_day = Math.floor(to_s / (24 * 60 * 60));
  409.             // FIXME: deal with sunday/monday week starts
  410.             var from_sow = from_day - from_time.getDay();
  411.             var to_sow = to_day - to_time.getDay();
  412.             if (from_day == to_day) {
  413.                 return localizedStrings.get("general.dates.intervals.today");
  414.             } else if (to_day - from_day == 1) {
  415.                 return localizedStrings.get("general.dates.intervals.yesterday");
  416.             } else if (to_sow - from_sow <= 7) {
  417.                 var dow = localizedStrings.get("general.dates.weekday."
  418.                     + from_time.getDay());
  419.                 if (to_sow > from_sow) {
  420.                     dow = localizedStrings.get("general.dates.last", [ dow ]);
  421.                 }
  422.                 return dow;
  423.             } else {
  424.                 var from_som = from_day - (from_time.getDate() - 1);
  425.                 var to_som = to_day - (to_time.getDate() - 1);
  426.                 // Go by weeks
  427.                 if ((to_sow - from_sow <= 21) || (from_som == to_som)) {
  428.                     return localizedStrings.get("general.dates.ago", [ localizedStrings
  429.                         .get("general.dates.intervals.weeks", [ Math
  430.                             .round((to_sow - from_sow) / 7) ]) ]);
  431.                 } else {
  432.                     // Go by months
  433.                     if (to_som - from_som < 45) {
  434.                         return localizedStrings.get("general.dates.intervals.last_month");
  435.                     } else {
  436.                         var mname = localizedStrings.get("general.dates.month."
  437.                             + from_time.getMonth());
  438.                         if (from_time.getFullYear() != to_time.getFullYear()) {
  439.                             mname = localizedStrings.get("general.dates.month_with_year", [
  440.                                 mname, from_time.getFullYear() ]);
  441.                         }
  442.                         return mname;
  443.                     }
  444.                 }
  445.             }
  446.         }
  447.     },
  448.     
  449.     toXml: function(parent,object) {
  450.         var ret = parent;
  451.         if (typeof(parent) == "string") {
  452.             var d = document.implementation.createDocument("","",null);
  453.             parent = d.createElement(parent);
  454.             d.appendChild(parent);
  455.             ret = d;
  456.         }
  457.         var doc = (parent.nodeType == Components.interfaces.nsIDOMNode.DOCUMENT_NODE) ? parent : parent.ownerDocument;
  458.         var type = typeof object;
  459.         switch (type) {
  460.             case 'undefined':
  461.             case 'function':
  462.             case 'unknown': return;
  463.             case 'number':
  464.             case 'string':
  465.             case 'boolean': { 
  466.                 var t = doc.createTextNode(object.toString());
  467.                 parent.appendChild(t);
  468.                 return ret;
  469.             }
  470.         }
  471.     
  472.       if (object === null) {
  473.             return ret;
  474.         }
  475.       
  476.         if (Prototype.O.isArray(object)) {
  477.             var name = object["__itemName"] || "Item";
  478.             var l = object.length;
  479.             for (var i = 0; i < l; ++i) {
  480.                 var itemElem = doc.createElement(name);
  481.                 parent.appendChild(itemElem);
  482.                 Utils.toXml(itemElem,object[i]);
  483.             }
  484.             return ret;
  485.         }
  486.         
  487.         if (object instanceof Components.interfaces.nsIDOMNode) {
  488.             parent.appendChild(doc.importNode(object,true));
  489.             return ret;
  490.         }
  491.         
  492.         for (var p in object) {
  493.             if (object[p] !== null) {
  494.                 if (Prototype.S.startsWith(p,"@")) {
  495.                     parent.setAttribute(p.substring(1),object[p].toString());
  496.                 } else if (p === "__content") {
  497.                 Utils.toXml(parent,object[p]);
  498.                 } else {
  499.                     var e = doc.createElement(p);
  500.                     parent.appendChild(e);
  501.                     Utils.toXml(e,object[p]);
  502.                 }
  503.             }
  504.         }
  505.         return ret;
  506.     },
  507.     
  508.     getPixelsFromStyleSizeStr: function(size) {
  509.         var res = /^([0-9]+(?:\.[0-9]*)?)(?:px)?$/.exec(size);
  510.         return res === null ? null : parseFloat(res[1]); 
  511.     },
  512.     
  513.     getStyleSize: function(element,property) {
  514.         var res = Prototype.E.getStyle(element,property);
  515.         return res === null ? null : this.getPixelsFromStyleSizeStr(res);
  516.     },
  517.         
  518.     containsAllOf: function(text,searchStrings) {
  519.         return searchStrings.every(function(searchString) {
  520.             if (typeof(searchString) == "string") {
  521.                 return (text.indexOf(searchString) != -1);
  522.             } else {
  523.                 return this.containsAnyOf(text,searchString);
  524.             }
  525.         },this);
  526.     },
  527.     
  528.     containsAnyOf: function(text,searchStrings) {
  529.         return searchStrings.some(function(searchString) {
  530.             if (typeof(searchString) == "string") {
  531.                 return (text.indexOf(searchString) != -1);
  532.             } else {
  533.                 return this.containsAllOf(text,searchString);
  534.             }
  535.         },this);
  536.     },
  537.  
  538.     getClientWidth: function(element) {
  539.         if (element.clientWidth !== undefined) {
  540.             return element.clientWidth;
  541.         }
  542.         if (element.boxObject !== undefined) {
  543.             return element.boxObject.width - 
  544.                 (
  545.                         this.getStyleSize(element,"padding-left") + 
  546.                         this.getStyleSize(element,"padding-right") +
  547.                         this.getStyleSize(element,"border-left") +
  548.                         this.getStyleSize(element,"border-right")
  549.                 );
  550.         }
  551.         return null;
  552.     },
  553.     
  554.     getClientHeight: function(element) {
  555.         if (element.clientHeight !== undefined) {
  556.             return element.clientHeight;
  557.         }
  558.         if (element.boxObject !== undefined) {
  559.             return element.boxObject.height - 
  560.                 (
  561.                         this.getStyleSize(element,"padding-top") + 
  562.                         this.getStyleSize(element,"padding-bottom") +
  563.                         this.getStyleSize(element,"border-top") +
  564.                         this.getStyleSize(element,"border-bottom")
  565.                 );
  566.         }
  567.         return null;
  568.     },
  569.  
  570.     truncateNodeTextToMaxDimensions: function(truncNode, text, guessChars, maxHeight, maxWidth) {
  571.         if (!maxWidth && !maxHeight) {
  572.             throw "Must provide at least one of maximum width or maximum height";
  573.         }
  574.         text = Prototype.S.strip(text);
  575.         var nChars = text.length;
  576.         var minChars = 0;
  577.         var maxChars = nChars;
  578.         var curChars = (guessChars < 0) ? nChars : Math.max(nChars,Math.min(1,guessChars));
  579.         truncNode.textContent = text.substr(0,curChars)+((curChars < nChars) ? "..." : "");
  580.         var curh;
  581.         var curw;
  582.         var curv;
  583.         var maxv = maxHeight ? (maxWidth ? maxHeight * maxWidth : maxHeight) : maxWidth; 
  584.         while (maxChars > minChars) {
  585.             curv = 1;
  586.             if (maxHeight) {
  587.                 curv = this.getClientHeight(truncNode);
  588.             }
  589.             if (maxWidth) {
  590.                 curv = curv * this.getClientWidth(truncNode);
  591.             }
  592.             if ((maxHeight && this.getClientHeight(truncNode) > maxHeight) ||
  593.                  (maxWidth && this.getClientWidth(truncNode) > maxWidth)) {
  594.                 // If we have too much text, decrease the max limit
  595.                 maxChars = curChars - 1;
  596.             } else {
  597.                 // If we haven't hit the limit, increase the min limit
  598.                 minChars = curChars;
  599.             }
  600.             if (minChars >= maxChars) {
  601.                 curChars = maxChars;
  602.                 break;
  603.             }
  604.             // Try to guess the next best number of chars to use based on values
  605.             var minNext = Math.max(minChars,Math.floor(maxChars/3 + minChars*2/3));
  606.             if (minNext == curChars) {
  607.                 ++minNext;
  608.             }
  609.             var maxNext = Math.min(maxChars,Math.ceil(maxChars*2/3 + minChars/3));
  610.             if (maxNext == curChars) {
  611.                 --maxNext;
  612.             }
  613.             // Try to guess a value for the next number of chars to try based on
  614.             // dimensions
  615.             // and trim to reasonable bounds
  616.             curChars = Math.min(maxNext,Math.max(minNext,Math.round(curChars * maxv / curv)));
  617.             truncNode.textContent = text.substr(0,curChars)+((curChars < nChars) ? "..." : "");
  618.         }
  619.         var t = text.substr(0,curChars)+((curChars < nChars) ? "..." : "");
  620.         truncNode.textContent = t;
  621.         return t;
  622.     },
  623.     
  624.     Twitter: {
  625.         getUserURL: function(userName) {
  626.             return "http://twitter.com/" + encodeURIComponent(userName);
  627.         }
  628.     },
  629.     
  630.     md5: function(str) {
  631.         var converter =
  632.           Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].
  633.             createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
  634.  
  635.         // we use UTF-8 here, you can choose other encodings.
  636.         converter.charset = "UTF-8";
  637.         // result is an out parameter,
  638.         // result.value will contain the array length
  639.         var result = {};
  640.         // data is an array of bytes
  641.         var data = converter.convertToByteArray(str, result);
  642.         var ch = Components.classes["@mozilla.org/security/hash;1"]
  643.                            .createInstance(Components.interfaces.nsICryptoHash);
  644.         ch.init(ch.MD5);
  645.         ch.update(data, data.length);
  646.         var hash = ch.finish(false);
  647.  
  648.         return this.toHexString(hash);
  649.     },
  650.     
  651.     toHexString: function(bytes) {
  652.         var res = [];
  653.         var l = bytes.length;
  654.         for (var i = 0; i < bytes.length; ++i) {
  655.             res.push(Prototype.N.toPaddedString(bytes.charCodeAt(i),2,16));
  656.         }
  657.         return res.join('');
  658.     },
  659.  
  660.     getAncestorOrSelfAttributeOrProperty: function(elem,name) {
  661.         var val = elem.getAttribute(name);  
  662.         if (val) {
  663.             return val;
  664.         }
  665.         for (var p = elem.parentNode; p; p = p.parentNode) {
  666.             if (p[name]) {
  667.                 return p[name];
  668.             } else if (p.getAttribute && p.getAttribute(name)) {
  669.                 return p.getAttribute(name);
  670.             }
  671.         }
  672.         return null;
  673.     },
  674.     
  675.     getAncestorOrSelfAggregateAttributeOrProperty: function(elem,name) {
  676.         var val = elem.getAttribute(name);
  677.         var cur = elem;
  678.         while (!val || Prototype.S.startsWith(val,'$')) {
  679.             // We find the next value up the parent chain
  680.             var next = null;
  681.             for (var p = cur.parentNode; p; p = p.parentNode) {
  682.                 if (p[name]) {
  683.                     next = p[name];
  684.                     break;
  685.                 } else if (p.getAttribute && p.getAttribute(name)) {
  686.                     next = p.getAttribute(name);
  687.                     break;
  688.                 }
  689.             }
  690.             // If none was found, there is no value
  691.             // (note, even if the value starts with $,
  692.             // we consider it nonexistent if there is no matching parent
  693.             // value
  694.             if (!next) {
  695.                 return null;
  696.             }
  697.             // If the current value is empty, we simply
  698.             // take the parent. Otherwise, we replace the initial $ with
  699.             // the parent. Note that it may still begin with $, in which
  700.             // case the loop will continue
  701.             if (!val) {
  702.                 val = next;
  703.             } else {
  704.                 val = next + val.substring(1);
  705.             }
  706.         }
  707.         return val;
  708.     },
  709.  
  710.     isPrivateBrowsingEnabled: function() {
  711.         var pbs = null;
  712.         try {
  713.             pbs = Components.classes["@mozilla.org/privatebrowsing;1"]
  714.                                 .getService(Components.interfaces.nsIPrivateBrowsingService);
  715.         } catch (ex) {
  716.             return false;
  717.         }
  718.         if (!pbs) {
  719.             return false;
  720.         }
  721.         return pbs.privateBrowsingEnabled;
  722.     },
  723.  
  724. };
  725.  
  726. Utils.LocalizedBundle = Prototype.Class.create({
  727.     initialize: function(doc) {
  728.         this.bundle = doc.getElementById("glydo-stringbundle");
  729.         this.defaultBundle = doc.getElementById("glydo-default-stringbundle");
  730.     },
  731.  
  732.     get: function(key, args) {
  733.         var str = null;
  734.         try {
  735.             if (args === undefined) {
  736.                 try {
  737.                     str = this.bundle.getString(key);
  738.                 } catch (e) {
  739.                     str = this.defaultBundle.getString(key);
  740.                 }
  741.             } else {
  742.                 try {
  743.                     str = this.bundle.getFormattedString(key, args);
  744.                 } catch (e) {
  745.                     str = this.defaultBundle.getFormattedString(key, args);
  746.                 }
  747.             }
  748.         } catch (e) {
  749.             
  750.             return null;
  751.         }
  752.         return str;
  753.     },
  754.     
  755. });
  756.  
  757. Utils.ISO8601_DATE_FORMAT = "%Y-%m-%dT%H:%M:%S";
  758. Utils.ISO8601_DATE_PATTERN = /^(\d{4})-?(\d{2})-?(\d{2})T(\d{2}):?(\d{2}):?(\d{2})(?:\.(\d{3}))?(Z|([+-])(\d{2}):?(\d{2}))?$/;
  759. Utils.IMAGE_SCALING_CONSTS = {
  760.         MAX_THUMBNAIL_SCALING_FACTOR: 1.25,
  761.         MIN_THUMBNAIL_SCALING_FACTOR: 0.1,
  762. };
  763.  
  764. Utils.APP_INFO = Components.classes["@mozilla.org/xre/app-info;1"].getService(Components.interfaces.nsIXULAppInfo);
  765. Utils.HIGH_LEVEL_DOMAIN_PARAMS = {
  766.     domainPartsPerTLD: {
  767.       "aero" :2,
  768.       "asia" :2,
  769.       "biz" :2,
  770.       "cat" :2,
  771.       "com" :2,
  772.       "coop" :2,
  773.       "edu" :2,
  774.       "gov" :2,
  775.       "info" :2,
  776.       "int" :2,
  777.       "jobs" :2,
  778.       "mil" :2,
  779.       "mobi" :2,
  780.       "museum" :2,
  781.       "name" :2,
  782.       "net" :2,
  783.       "org" :2,
  784.       "pro" :2,
  785.       "tel" :2,
  786.       "travel" :2,
  787.       "post" :2,
  788.       "geo" :2,
  789.       "cym" :2,
  790.       "fm": 2,
  791.       "am": 2,
  792.       "tv": 2,
  793.       "cd": 2,
  794.       "dj": 2,
  795.       "mu": 2
  796.     },
  797.     nDomainPartsUnrecognizedTLD: 3
  798. };
  799.  
  800.  
  801.